package com.songtech.uitest.util;

import com.songtech.uitest.components.IBrowserDriverFactory;
import com.songtech.uitest.entities.Indent;
import com.songtech.uitest.module.common.LogInAndOutHandler;
import com.songtech.uitest.module.common.TabHandler;
import com.songtech.uitest.resources.AssistResource;
import com.songtech.uitest.resources.ServerResource;
import com.songtech.uitest.resources.ThirdResource;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.testng.*;
import org.testng.annotations.Test;
import org.testng.internal.BaseTestMethod;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Set;

/**
 * 在一个测试方法中，若有多个关键点（不可回退重来的地方，如数据提交的操作）
 * 为了实现发生异常时能记录本次位置，重试时能跳过之前做过的关键点，从其之后重试
 */
@Component
public class RollbackHelper {

    @Autowired
    @Qualifier("browserDriver")
    private IBrowserDriverFactory driverCreater;

    @Autowired
    private LogInAndOutHandler logInAndOutHandler;

    @Autowired
    private TabHandler tabHandler;

    @Autowired
    private ModulesMapper mapper;

    @Autowired
    private ServerResource serverResource;

    @Autowired
    private AssistResource assistResource;

    @Autowired
    private ThirdResource thirdResource;

    @Autowired
    private Indent indent;

    private Logger logger = LoggerFactory.getLogger(RollbackHelper.class);

    @Autowired
    private Space space;

    //标识传入的方法已经被成功执行了几次。回滚重来时，从执行成功后的位置做起
    private int counter = 0;

    public int getCounter() {
        return counter;
    }

    public void setCounter(int counter) {
        this.counter = counter;
    }

    //object是传入的待管理和调用的对象
    //method是传入的待管理和调用的方法名称。
    //flow是类似"00,01"的参数组
    //argsGroup是多组参数Object[Object[],object[]]，每组是Object[]形式的参数。
    //当前传入的是类似Object[Object["00"],object["01"]]的数组
    //方法作用：根据传进来的"00,01"格式的参数，运行指定的方法
    public void run(Object object, String methodName, String flow) throws Exception{

        //把类似"00,01"的参数组转换为类似Object[Object["00"],object["01"]]的数组，反射要用这样的类型。
        Object[] array = flow.split(","); //Object["00","01"]
        Object[][] argsGroup = new Object[array.length][];
        for(int i=0;i<array.length;i++){
            argsGroup[i] = new Object[]{array[i]};
        }


        Method method;
        for(int i=0; i<argsGroup.length; i++){
            //快进到counter的位置
            i = counter;

            //得到一组参数转换成的参数class类型组
            Object[] args = argsGroup[i];
            Class[] argsClass = new Class[args.length];
            for (int j = 0; j < args.length; j++) {
                argsClass[j] = args[j].getClass();
            }
            //得到方法
            method = object.getClass().getMethod(methodName, argsClass);

            //用此组参数调用方法
            method.invoke(object, args);
            //标识已成功进行过了几组
            counter++;
        }
        //在有限的重试机会里，若最终能执行完毕，恢复计数器到0
        //若没能执行完就把重试次数用完了，则没机会执行这一句，交由Retry对象帮助归0
        counter = 0;

    }

    /**
     * flow为流程。格式类似：11100,11200, ...。前三位代表执行某流程类的某方法；第四位代表是否通过，第五位代表由谁做
     * @param flow
     * @throws Exception
     */
    public void run(String flow) throws Exception {
        String moduleClassMethodName = "";
        String moduleClassName = "";
        String moduleMethodName = "";
        String moduleLowerCaseClassName = "";

        Object moduleObject = null;
        Method method;
        String step = "";

        String[] flowArray = flow.split(",");
        for(int i=0; i<flowArray.length; i++) {

            i = counter;

            step = flowArray[i]; //step: 类似11100

            moduleClassMethodName = mapper.getClassMethod(step.substring(0, 3));
            moduleClassName = StringUtils.substringBefore(moduleClassMethodName, ".");
            moduleLowerCaseClassName = new StringBuilder().append(Character.toLowerCase(moduleClassName.charAt(0))).append(moduleClassName.substring(1)).toString();
            moduleMethodName = StringUtils.substringAfter(moduleClassMethodName, "."); //反射要用到的方法名

            String param = step.substring(3);
            //Object[][] paramsArray = new Object[][]{{param}}; //反射要用到的参数数组
            Object[] paramsArray = new Object[]{param};
            //Class[] paramsClass = new Class[paramsArray.length];  //反射要用到的参数.class数组
//            for (int j = 0; j < paramsArray.length; j++) {
//                paramsClass[j] = paramsArray[j].getClass();
//            }

            Class[] paramsClass = new Class[]{String.class};


            moduleObject = SpringUtil.getBean(moduleLowerCaseClassName); //反射要用到的对象

            //东西都齐了，等待反射调用此方法，并且仿照run方法做好出错时的回溯
            //每一个step执行对应类的方法，
            method = moduleObject.getClass().getMethod(moduleMethodName, paramsClass);
            method.invoke(moduleObject, paramsArray);

            counter++;

            //这段代码是之前想改造reportNG的结果，希望人为的加上每次循环时的方法名作为输出结果项
            //最后因ITestResult完全无法人为的新建而放弃
//            ITestResult tr = Reporter.getCurrentTestResult();
//            BaseTestMethod baseTestMethod = (BaseTestMethod) tr.getMethod();
//            Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName");
//            f.setAccessible(true);
//            f.set(baseTestMethod, moduleClassMethodName);
//
//
//            tr.getTestContext().getPassedTests().addResult(tr, tr.getMethod());
//            f.set(baseTestMethod, "xxx.xxxx");
//            tr.getTestContext().getPassedTests().addResult(tr, tr.getMethod());
//
//            System.out.println("^^^^^^^^^^^^^^^^^passed^^^^^^^^^^^^^^^^^^^");
//            Set set = tr.getTestContext().getPassedTests().getAllResults();
//            Iterator<ITestResult> iter = set.iterator();
//            while (iter.hasNext()){
//                ITestResult it = iter.next();
//                System.out.println("------->" + it.getMethod().getMethodName());
//            }
//            System.out.println("^^^^^^^^^^^^^^^^^failed^^^^^^^^^^^^^^^^^^^");
//            Set set2 = tr.getTestContext().getFailedTests().getAllResults();
//            Iterator<ITestResult> iter2 = set2.iterator();
//            while (iter2.hasNext()){
//                ITestResult it2 = iter2.next();
//                System.out.println("------->" + it2.getMethod().getMethodName());
//            }
//            System.out.println("^^^^^^^^^^^^^^^^^skipped^^^^^^^^^^^^^^^^^^^");
//            Set set3 = tr.getTestContext().getFailedTests().getAllResults();
//            Iterator<ITestResult> iter3 = set3.iterator();
//            while (iter3.hasNext()){
//                ITestResult it3 = iter3.next();
//                System.out.println("------->" + it3.getMethod().getMethodName());
//            }
//            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");


            rollbackPreDriverToInit();
            //判断一下，免得多余执行重置
            int nextI = i + 1;
            //如果当前循环是最后一个步骤
            if(nextI >= flowArray.length){
                continue;
            }

            String nextStep = flowArray[nextI];
            String next = nextStep.substring(4);//取得最后一位
            String current = step.substring(4);
            if(!next.equals(current)){
                rollbackCurrentDriverToInit();
            }



        }

        counter = 0;

    }




    /**
     * 让两个浏览器都回到初始状态，方便操作
     * 若是firefox,期望回到登录后的初始主页状态(默认是主账号登录)
     * 若是chrome,期望回到退出后，初始化选择进入铁塔模块的界面
     *
     */
    public void rollbackAllDriverToInit() throws Exception{
        rollbackMainDriverToInit();
        rollbackViceDriverToInit();
        rollbackThirdDriverToInit();

    }

    //跑完一个步骤后，让这个浏览器回到初始状态
    public void rollbackPreDriverToInit() throws Exception{
        //每一次初始化时，缩进空格归0
        space.delAllSpace();

        //判断到流程最开头没有，没到才重置,否则啥都不做了
        if(indent.getStepPosition() - 1 < 0){
            return;
        }

        logger.info("已执行完流程的浏览器初始化开始...");

        String step = indent.getFlow().split(",")[indent.getStepPosition() - 1];

        int accountNum = Integer.parseInt(step.substring(4));
        if(accountNum == 0){ //主浏览器还原
            rollbackMainDriverToInit();
        }
        else if(accountNum == 9){ //第三方浏览器还原
            rollbackThirdDriverToInit();
        }
        else{ //辅浏览器还原
            rollbackViceDriverToInit();
        }
//        Thread.sleep(6000);
    }

    //跑完一个步骤后，让下一个浏览器到初始状态
    public void rollbackCurrentDriverToInit() throws Exception{
        //每一次初始化时，缩进空格归0
        space.delAllSpace();

        //判断到流程末尾没有，没到才重置,否则啥都不做了
        if(indent.getStepPosition() >= indent.getFlow().split(",").length){
            return;
        }

        logger.info("接下来要执行流程的浏览器初始化开始...");

        String step = indent.getFlow().split(",")[indent.getStepPosition()];

        int accountNum = Integer.parseInt(step.substring(4));
        if(accountNum == 0){ //主浏览器还原
            rollbackMainDriverToInit();
        }
        else if(accountNum == 9){ //第三方浏览器还原
            rollbackThirdDriverToInit();
        }
        else{ //辅浏览器还原
            rollbackViceDriverToInit();
        }
//        Thread.sleep(6000);

    }

//    public void rollbackMainDriverToInit() throws Exception{
//        WebDriver driver = driverCreater.getMainDriver();
//        //初始化主浏览器
//        try {
//            driver.switchTo().defaultContent();
//            driver.get(serverResource.getLoginPath());
//            Thread.sleep(5000);
//            driver.findElement(By.cssSelector("div.exit.first"));
//            tabHandler.closeAllTabs(driver);
//        }
//        catch (Exception e){ //出错了说明没登录
//            driver.navigate().refresh(); //防止在登录输入页
//            Thread.sleep(2000);
//            logInAndOutHandler.login(driver, 0); //默认是主账号
//        }
//        logger.info("主浏览器回到主账号登录后的初始主页");
//
//    }

    public void rollbackMainDriverToInit() throws Exception{
        WebDriver driver = driverCreater.getMainDriver();
        AutoWait.setWebDriverWait(driver);
        driver.switchTo().defaultContent();
        driver.get(serverResource.getLoginPath());
        WebElement element = AutoWait.find1In2(By.cssSelector("div.exit.first"), By.cssSelector("div.left"));
        //如果是未登录的初始页
        if(element.getAttribute("class").equals("left")){
            logInAndOutHandler.login(driver, 0); //主账号登录
        }
        logger.info("主浏览器回到主账号登录后的初始主页");

    }

//    public void rollbackViceDriverToInit() throws Exception{
//        WebDriver driver = driverCreater.getViceDriver();
//        //初始化辅浏览器:
//        try {
//            driver.switchTo().defaultContent();
//            driver.get(assistResource.getLoginPath().split(",")[0]);
//            Thread.sleep(5000);
//            WebElement div = driver.findElement(By.cssSelector("div.exit.first"));
//            div.click();
//            Thread.sleep(1000);
//            driver.findElement(By.id("signoutAccount")).click();
//            Thread.sleep(3000);
//        }
//        catch (Exception e){ //出错了说明没登录
//            driver.navigate().refresh(); //防止在登录输入页
//            Thread.sleep(2000);
//        }
//        logger.info("辅浏览器回到系统未登录时的初始页面");
//    }

    public void rollbackViceDriverToInit() throws Exception{
        WebDriver driver = driverCreater.getViceDriver();
        AutoWait.setWebDriverWait(driver);
        driver.switchTo().defaultContent();
        driver.get(assistResource.getLoginPath().split(",")[0]);
        WebElement element = AutoWait.find1In2(By.cssSelector("div.exit.first"), By.cssSelector("div.left"));
        //如果是在已登录的首页
        if(element.getAttribute("class").equals("exit first")){
            element.click();
            AutoWait.findByID("signoutAccount").click();
        }
        logger.info("辅浏览器回到系统未登录时的初始页面");

    }

    public void rollbackThirdDriverToInit() throws Exception{
        WebDriver driver = driverCreater.getThirdDriver();
        //初始化第三方浏览器：
        driver.switchTo().defaultContent();
        driver.get(thirdResource.getLoginPath());

        logInAndOutHandler.login(driver, 9); //默认是第三方账号

        logger.info("第三方浏览器回到主账号登录后的初始主页");
    }



    /**
     * 根据指定的浏览器作初始化操作
     * @param driver 传入的WebDriver
     * @throws Exception
     */
//    public void rollbackToInit(WebDriver driver) throws Exception{
//
//        if(driver.getClass().getSimpleName().equals("FirefoxDriver")){
//            //初始化主浏览器firefox：
//            try {
//                driver.switchTo().defaultContent();
//                driver.findElement(By.cssSelector("div.exit.first"));
//                tabHandler.closeAllTabs(driver);
//            }
//            catch (Exception e){ //出错了说明没登录
//                driver.navigate().refresh(); //防止在登录输入页
//                Thread.sleep(2000);
//                logInAndOutHandler.login(driver, 0); //默认是主账号
//            }
//            logger.info("firefox浏览器回到主账号登录后的初始主页");
//        }
//
//        if(driver.getClass().getSimpleName().equals("ChromeDriver")){
//            //初始化辅浏览器chrome:
//            try {
//                driver.switchTo().defaultContent();
//                WebElement div = driver.findElement(By.cssSelector("div.exit.first"));
//                div.click();
//                Thread.sleep(1000);
//                driver.findElement(By.id("signoutAccount")).click();
//                Thread.sleep(3000);
//            }
//            catch (Exception e){ //出错了说明没登录
//                driver.navigate().refresh(); //防止在登录输入页
//                Thread.sleep(2000);
//            }
//            logger.info("chrome浏览器回到系统未登录时的初始页面");
//        }
//
//        if(driver.getClass().getSimpleName().equals("InternetExplorerDriver")){
//            //初始化第三方浏览器IE：
//            try {
//                driver.switchTo().defaultContent();
//                driver.findElement(By.cssSelector("div.exit.first"));
//                tabHandler.closeAllTabs(driver);
//            }
//            catch (Exception e){ //出错了说明没登录
//                driver.navigate().refresh(); //防止在登录输入页
//                Thread.sleep(3000);
//                logInAndOutHandler.login(driver, 0); //默认是主账号
//            }
//            logger.info("IE浏览器回到主账号登录后的初始主页");
//        }
//
//    }



}
